#import <netinet/in.h>

#import "GameClient.h"
#import "NetServiceBrowser.h"
#import "NetUtilities.h"
#import "Multiplayer_Internal.h"

@interface GameClient (Internal) <GamePeerDelegate, NetServiceBrowserDelegate>
@end

#pragma mark Helpful Functions

static void _DictionaryApplierFunction(const void* key, const void* value, void* context)
{
	GamePeer*				server = (GamePeer*)value;
	NSMutableArray*			array = (NSMutableArray*)context;
	GamePeer*				peer;
	
	for(peer in array) {
		if(![[peer uniqueID] isEqualToString:[server uniqueID]])
            continue;
		
		if(([peer isConnecting] || [peer isConnected]) && ([server isConnecting] || [server isConnected]))
            [NSException raise:NSInternalInconsistencyException format:@""];
		if([peer isService] == [server isService])
            [NSException raise:NSInternalInconsistencyException format:@""];
		
		if([server isConnecting] || [server isConnected] || [server isService]) {
			[array removeObject:peer];
			break;
		}
		else
		return;
	}
	
	[array addObject:server];
}


#pragma mark Implementation

@implementation GameClient

#pragma mark General Functions

- (id) init
{
	if((self = [super init])) {
        _delegate = nil;
	}
	
	return self;
}

- (void)setDelegate:(NSObject<GameClientDelegate>*)d
{  
    
    if (_delegate != nil)
        [_delegate release];
    _delegate = [d retain];
    
}

- (void) dealloc
{
    [self stopDiscoveringServers];
    
    [_delegate release];
    
	[super dealloc];
}

#pragma mark Bonjour Discovery Functions

- (BOOL) startDiscoveringServersWithIdentifier:(NSString*)identifier
{
    
	if(_onlineServers || !_IsValidGameIdentifier(identifier))
        return NO;
	
	_onlineServers = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
	
    _initBrowserComplete = NO;
    [NSThread detachNewThreadSelector:@selector(initBrowser:) toTarget:self withObject:identifier];
    
    BOOL ready = NO;
    while (ready == NO)
    {
        @synchronized (self) {
            ready = _initBrowserComplete;
        }
    }
    
    if (_browser == nil)
        return NO;
        
    [_delegate performSelectorOnMainThread:@selector(gameClientDidStartDiscoveringServers:) withObject:self waitUntilDone:YES];
    
    
	return YES;
}

- (void) initBrowser:(NSString*)identifier
{
    NSAutoreleasePool       * pool = [[NSAutoreleasePool alloc] init];
    
	_browser = [[NetServiceBrowser alloc] initTCPBrowserWithDomain:nil applicationProtocol: identifier];
	[_browser setDelegate:self];
	if(![_browser startUsingRunLoop:[NSRunLoop currentRunLoop]]) {
		[_browser release];
		_browser = nil;
		CFRelease(_onlineServers);
		_onlineServers = NULL;
	}
    
    @synchronized (self) {
        _initBrowserComplete = YES;
    }
    [pool release];
    
    CFRunLoopRun();
}

- (BOOL) isDiscoveringServers
{
	return (_onlineServers ? YES : NO);
}

- (NSArray*) onlineServers
{
	NSMutableArray*			array = nil;

	if(_onlineServers) {
		array = [NSMutableArray array];
		CFDictionaryApplyFunction(_onlineServers, _DictionaryApplierFunction, array);
	}
	
	return array;
}

- (void) stopDiscoveringServers
{
    
	if(_onlineServers) {
    
        [_delegate performSelectorOnMainThread:@selector(gameClientWillStopDiscoveringServers:) withObject:self waitUntilDone:YES];
		
		[_browser stop];
		[_browser release];
		_browser = nil;
		
		CFRelease(_onlineServers);
		_onlineServers = NULL;
	}
    
}

@end

@implementation GameClient (NetServiceBrowserDelegate)

- (void) browser:(NetServiceBrowser*)server didAddService:(CFNetServiceRef)service
{
	GamePeer*						peer;
	
    
	peer = [[GamePeer alloc] initWithCFNetService:service];
	if(peer) {
		CFDictionarySetValue(_onlineServers, service, peer);
		[peer release];

        [_delegate performSelectorOnMainThread:@selector(gameClientDidUpdateOnlineServers:) withObject:self waitUntilDone:YES];
	} else
        REPORT_ERROR(@"Failed creating GamePeer from %@. Possibly because the remote service name did not have a : in it. The format expected by these libraries is DEVICE_ID:name.", service);
    
    
}

- (void) browser:(NetServiceBrowser*)server didRemoveService:(CFNetServiceRef)service
{
    
	CFDictionaryRemoveValue(_onlineServers, service);
    [_delegate performSelectorOnMainThread:@selector(gameClientDidUpdateOnlineServers:) withObject:self waitUntilDone:YES];
    
}

@end
